"""
# -*- coding: utf-8 -*-

Created on 2021/5/28 15:33

@File: dispatcher.py
@Author: LT
@Description: URL方面的总的调度员，负责处理各个页面交互的先后顺序，从而保证有条不紊
"""
from ...about_logging.common_logging import Logging
from ...common.read_settings import SettingsWrapper
from ...about_excel.extract_from_data import ExcelDataExtractor
from ..about_1_create_a_new_station.create_a_new_station import CreateNewStation        # 新建站点
from ..about_2_create_a_new_renter.create_a_new_renter import CreateNewRenter           # 新增租户
from ..about_3_create_a_new_customer.create_a_new_customer import CreateNewCustomer     # 新增客户
from ..about_4_1_search_station_objectid.search_station_objectid import SearchStationObjectID
from ..about_4_append_this_station_into_organization.append_this_station_into_organization import AppendStationIntoOrganization
from ..about_5_initialize_database_and_synchronize_station.initialize_database_and_synchronize_station import SynchronizeStation, InitializeDatabase
from ..about_6_1_query_api_for_siteid.query_api_for_siteid import QueryApiForSiteId
from ..about_6_set_triple_relation.set_triple_relation import SetTripleRelation         # 三角站点关联关系维护
from ..about_7_set_Lng_and_Lat.set_Lng_and_Lat import SetLngLat                         # 前端页面下的坐标值录入
from ..about_8_export_stationname_account_pwd.export_stationname_account_pwd import Exporter    # 导出账号、密码等信息
from ...about_database.query_database import QueryDatabase
import time
from threading import Thread, Lock          # 线程类，锁
from queue import Queue                     # 队列类


class Dispatcher(SettingsWrapper):
    def __init__(self):
        SettingsWrapper.__init__(self)
        self.log = Logging().getlog()                           # 获取日志对象，格式啊，放哪啊，都在对应模块弄好了
        self.query_database = QueryDatabase()                   # 数据库查询器
        self.excel_data_list = ExcelDataExtractor().data_list   # 获取从excel表中提取的经校验后的数据内容
        self.renter_suffix = '（租户）'                             # 租户的尾缀
        self.customer_suffix = '（客户）'                           # 客户的尾缀
        self.all_ids_list = []           # 列表字典，每个字典元素内含：station_name、objectId、siteId、userId1、userId2
        self.objectId_list = []          # 这是为了后面添加进组织结构时，方便使用的objectId列表
        pass

    def start(self):  # 用来执行各模块，不同错误有不同的应对；首先采用单线程
        try:
            self.log.info('-->Start in dispatcher<--')

            # 每次都将self.excel_data_list中的一个个元素放入到CreateNewStation实例接口中，并分析请求结果
            for each_station_info in self.excel_data_list:
                kwargs_dict = self.__pack_each_station_info_func(each_station_info)
                station_name = kwargs_dict.get('station_name')          # 取出目标站点名字，以作后续日志记录使用
                longitude = kwargs_dict.get('longitude')                # 从经纬度到县区代码是为了扩充字典，作后面的坐标填入用
                latitude = kwargs_dict.get('latitude')
                address = kwargs_dict.get('address')
                regionCode = kwargs_dict.get('regionCode')

                #################################################
                # 开始新建站点，并跳过与已有站点名重复的站点
                # 旧版1.0是通过查询从库数据库t_site中是否已有目标站点名字来判断重复与否的
                # 新版2.0是直接通过后台站点关联关系维护的接口访问来判定
                #################################################
                # res = self.query_database.execute_select('t_site', 'site_name', station_name)
                # if len(res) > 0:
                #     self.log.warning(f"该站点->{station_name} 与已有站点重复，已被跳过，请修改名字后再试！'")
                #     continue

                if self.__station_overlap_checker(station_name):
                    self.log.warning(f"该站点->{station_name} 与已有站点重复，已被跳过，请修改名字后再试！'")
                    continue

                station = CreateNewStation(**kwargs_dict)
                if str(station.res.json().get('retCode')) == '10000':
                    self.log.info(f'{station_name}新建站点成功！')
                else:
                    self.log.warning(f'{station_name}新建站点失败!失败代码是：{station.res.json()}')
                    continue

                #################################################
                # 开始新建租户——Renter
                #################################################
                renter = CreateNewRenter(**kwargs_dict)

                if renter.params_dict:
                    self.log.info(f'{station_name}新建租户成功！')
                    kwargs_dict["renter_account"] = renter.params_dict.get("renter_account")
                    kwargs_dict["renter_password"] = renter.params_dict.get("renter_password")
                    kwargs_dict["renter_userId"] = renter.params_dict.get("renter_userId")
                    kwargs_dict["renter_companyId"] = renter.params_dict.get("renter_companyId")
                else:
                    self.log.warning(f'{station_name}新建租户失败!失败代码是：{renter.res.json()}')
                    continue

                #################################################
                # 开始新建客户——Customer
                #################################################
                # time.sleep(2)       # 强制休眠两秒，为了让数据写入数据库，保险起见，慢一点，稳一点，已在创立租户和客户的时候各等1秒了
                customer = CreateNewCustomer(**kwargs_dict)
                if customer.params_dict:
                    self.log.info(f'{station_name}新建客户成功！')
                    kwargs_dict["customer_userId"] = customer.params_dict.get("customer_userId")
                    kwargs_dict["customer_companyId"] = customer.params_dict.get("customer_companyId")
                else:
                    self.log.warning(f'{station_name}新建客户失败!失败代码是：{customer.res.json()}')
                    continue

                #################################################
                # 开始完善最后一步站点、租户、客户三者关联所需的信息字典
                #################################################
                objectId = SearchStationObjectID(station_name).analyse()
                if not objectId:
                    self.log.warning(f'{station_name}获取objectId失败！已跳过，请手动完成此站点接下来的步骤。')
                    continue

                # 有可以不用从库数据库获得相应objectId和userId的办法————还是通过请求接口，接口对接的是主库，就不受对方未同步的影响了
                # renter_userId = self.query_database.execute_select('t_user', 'shortName', station_name + self.renter_suffix)[-1]['userId']
                # customer_userId = self.query_database.execute_select('t_user', 'shortName', station_name + self.customer_suffix)[-1]['userId']

                renter_userId = kwargs_dict.get("renter_userId")
                customer_userId = kwargs_dict.get("customer_userId")
                renter_account = kwargs_dict.get("renter_account")
                renter_password = kwargs_dict.get("renter_password")
                self.objectId_list.append(objectId)         # 后面统一添加进组织结构用的列表
                self.all_ids_list.append(dict(              # 按处理站点的顺序来生成三角关系的字典
                    station_name=station_name,
                    objectId=objectId,
                    userId1=renter_userId,                 # 租户的userId在客户实例创建的时候已经获取到了
                    userId2=customer_userId,                 # 客户的userId
                    longitude=longitude,
                    latitude=latitude,
                    address=address,
                    regionCode=regionCode,
                    renter_account=renter_account,
                    renter_password=renter_password,
                ))
                # 字典中还缺一个siteId，也就是excel_id，需要同步每个objectId，再initialSite之后，才能再数据库中获得

            if self.all_ids_list:       # 只有列表不为空的时候才进行接下来的操作
                #################################################
                # 有了self.all_ids_list就可以着手添加进组织结构了
                #################################################
                appender = AppendStationIntoOrganization(*self.objectId_list)
                if str(appender.res.json().get('retCode')) != '10000':
                    self.log.warning('添加进组织结构失败！请手动完成后续步骤。')
                    raise Exception('添加进组织结构失败！请手动完成后续步骤。')

                #################################################
                # 把所有站点的初步字典都弄完后——再分10个线程来用objectId同步站点
                #################################################
                synchronize_queue = Queue()
                synchronize_threads_num = 10
                if len(self.all_ids_list) < 10:
                    synchronize_threads_num = len(self.all_ids_list)
                synchronize_threads_list = []
                for each_dict in self.all_ids_list:
                    synchronize_queue.put(each_dict)

                def little_synchronization(thread_id):
                    while not synchronize_queue.empty():
                        obj_id = synchronize_queue.get()
                        exp = SynchronizeStation(obj_id)
                        if not exp.res.ok:
                            self.log.warning(f'Thread-{thread_id}同步objectId->{obj_id}失败！')

                for num in range(synchronize_threads_num):
                    thread = Thread(target=little_synchronization, args=(num,))
                    thread.start()
                    synchronize_threads_list.append(thread)

                # 等待所有同步功用的线程完成
                for each_thread in synchronize_threads_list:
                    each_thread.join()

                #################################################
                # 最后再initSiteInfo初始化站点信息
                #################################################
                little_initSiteInfo = InitializeDatabase()
                if not little_initSiteInfo.res.ok:
                    self.log.warning('initSiteInfo初始化站点信息失败！')
                    raise Exception('initSiteInfo初始化站点信息失败！请手动完成后续步骤。')

                #################################################
                # 用每个字典里的objectId来去数据库中获得siteId，来补全列表字典，这里objectId是不会为空的，因为空的前几步骤就引发错误了
                # 按理说，这里可以不需要错误判定了，因为前面没出问题，这里就不会出问题。
                #################################################
                for each_dict in self.all_ids_list:
                    stationname = each_dict.get("station_name")
                    userSiteInfos = QueryApiForSiteId(stationname)
                    if userSiteInfos.res.json().get("msg") == 'SUCCESS':
                        if len(userSiteInfos.res.json().get("data").get("site")) != 0:
                            for each in userSiteInfos.res.json().get("data").get("site"):
                                if each.get("site_name") == stationname:
                                    each_dict['siteId'] = each.get("site_id")
                                    self.log.info(f"{stationname} 站点的siteId查询成功！")
                                    break
                        else:
                            self.log.warning(f"{stationname} 站点的siteId查询失败--->查不到此站点名！")
                    else:
                        self.log.warning(f"{stationname} 站点的siteId查询失败--->查询连接建立失败！")
                        raise Exception(f"{stationname} 站点的siteId查询失败--->查询连接建立失败！")

                    # site_id = self.query_database.execute_select('t_site', 'objectId', obj_id)[-1].get('site_id')
                    #
                    # each_dict['siteId'] = site_id

                #################################################
                # 用补全列表字典，来完成最后的三角关系
                #################################################
                for each in self.all_ids_list:
                    exp = SetTripleRelation(**each)
                    if exp.res.json().get('msg') == 'SUCCESS':
                        self.log.info(f'{each["station_name"]}站点关联关系设置成功！')
                    else:
                        self.log.info(f'{each["station_name"]}站点关联关系设置失败！失败代码是:{exp.res.json()}')

                #################################################
                # 待完成三角关系后，用补全列表字典，来完成各个租户前端下的坐标录入
                #################################################
                for each in self.all_ids_list:
                    exp = SetLngLat(**each)
                    if exp.res.json().get('msg') == '修改成功！':
                        self.log.info(f'{each["station_name"]}站点坐标录入成功！')
                    else:
                        self.log.info(f'{each["station_name"]}站点坐标录入失败！失败代码是:{exp.res.json()}')

                #################################################
                # 全都完成后就是导出信息的问题了，格式为：站点名 账号 密码 objectId siteId
                #################################################
                exporter = Exporter(self.all_ids_list)
                exporter.process()

        except Exception as e:
            self.log.warning('failed!', e)
            return False
        else:
            self.log.info(f'Done!共完成{len(self.all_ids_list)}个站点的建立！')
            return True
        finally:
            self.log.info('-->End of in dispatcher<--\n')

    def __pack_each_station_info_func(self, each_station_info):
        """因为每个步骤的接口大体一致，故采用这种方法来解包传入"""
        # 根据county来分析出对应创建租户、客户时需要的regionCode
        for key_name in self.renter_and_customer_dict.keys():
            if key_name in str(each_station_info['县域（county）']):
                regionCode = self.renter_and_customer_dict[key_name]
                break
        else:
            regionCode = self.renter_and_customer_dict['瑞安']  # 若匹配不上，则默认为瑞安

        res = dict(station_name=str(each_station_info['场站名称（name）']),
                   doornumber=str(each_station_info['用采系统户号（doornumber）']),
                   doorname=str(each_station_info['用采系统户名（doorname）']),
                   longitude=str(each_station_info['经度（longitude）']),
                   latitude=str(each_station_info['纬度（latitude）']),
                   address=str(each_station_info['地址（address）']),
                   province=str(each_station_info['省（province）']),
                   city=str(each_station_info['市（city）']),
                   county=str(each_station_info['县域（county）']),
                   regionCode=str(regionCode),
                   streettown=str(each_station_info['街道村镇（streettown）']),
                   powersupplyarea=str(each_station_info['供电区域（powersupplyarea）']),
                   manager=str(each_station_info['负责人（manager）']),
                   tel=str(each_station_info['负责人电话（tel）']),
                   regisId='时间戳:' + str(round(time.time())),                        # 需要保证在18个字符以内，太长会出错
                   gatewayvender=str(each_station_info['网关厂家（gatewayvender）']),
                   admin=self.renter_and_customer_dict['管理员姓名'],
                   admin_phone=self.renter_and_customer_dict['管理员手机号'],
                   admin_email=self.renter_and_customer_dict['管理员email'],
                   renter_suffix=self.renter_suffix,
                   customer_suffix=self.customer_suffix,
                   )
        return res

    def __station_overlap_checker(self, station_name):
        """输入站点名字，通过接口来查重"""
        overlap_query = QueryApiForSiteId(station_name)
        if overlap_query.res.json().get("msg") == "SUCCESS":
            if len(overlap_query.res.json().get("data").get("site")) != 0:
                for each in overlap_query.res.json().get("data").get("site"):
                    if station_name == each.get("site_name"):
                        return True
                else:
                    return False
            else:  # 若等于0直接忽略，说明连可能重复的站点都没有
                return False
